#include <jni.h>
#include <unistd.h>
#include <string.h>
#include <android/log.h>
#include <linux/input.h>

#define LOG_TAG "wjp.native-lib"

#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <math.h>
#include <sys/ioctl.h>
#include <dirent.h>

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define TEST_BIT(bit, array) ((array[(bit) / 8] >> ((bit) % 8)) & 1)
typedef struct {
    int x;
    int y;
} Point;

void send_event(int fd, __u16 type, __u16 code, __s32 value) {
    struct input_event ev;
    memset(&ev, 0, sizeof(struct input_event));
    gettimeofday(&ev.time, NULL);
    ev.type = type;
    ev.code = code;
    ev.value = value;
    write(fd, &ev, sizeof(struct input_event));
}

float randomNum(float min, float max) {
    return min + ((float) rand() / (float) RAND_MAX) * (max - min);
}

Point *bezierCreate(int x1, int y1, int x2, int y2,
                    int x3, int y3, int x4, int y4,
                    int numberOfPoints, int *mh, int *outSize) {
    float h = randomNum(0, 300.0f);
    if (mh != NULL) {
        h = (float) (*mh);
    }

    float cp[4][2] = {
            {x1, y1 + h},
            {x2, y2 + h},
            {x3, y3 + h},
            {x4, y4 + h}
    };

    Point *curve = (Point *) malloc(sizeof(Point) * numberOfPoints);
    float dt = 1.0f / (numberOfPoints - 1);

    for (int i = 0; i < numberOfPoints; i++) {
        float t = dt * i;
        float t2 = t * t;
        float t3 = t2 * t;

        float cx = 3.0f * (cp[1][0] - cp[0][0]);
        float bx = 3.0f * (cp[2][0] - cp[1][0]) - cx;
        float ax = cp[3][0] - cp[0][0] - cx - bx;

        float cy = 3.0f * (cp[1][1] - cp[0][1]);
        float by = 3.0f * (cp[2][1] - cp[1][1]) - cy;
        float ay = cp[3][1] - cp[0][1] - cy - by;

        float resultX = ax * t3 + bx * t2 + cx * t + cp[0][0];
        float resultY = ay * t3 + by * t2 + cy * t + cp[0][1];

        curve[i].x = (int) (resultX);
        curve[i].y = (int) fabs(h - resultY);
    }

    *outSize = numberOfPoints;
    return curve;
}

// 随机控制点计算 + 生成贝塞尔轨迹
Point *random_points(int sx, int sy, int ex, int ey,
                     int num, int *mLeaveHeightLength, int *mh, int *outSize) {
    float leaveHeightLength = randomNum(0, 1000.0f);
    if (mLeaveHeightLength != NULL) {
        leaveHeightLength = (float) (*mLeaveHeightLength);
    }

    int x2, y2, x3, y3;

    if (abs(ex - sx) > abs(ey - sy)) {
        float my = (sy + ey) / 2.0f;
        y2 = (int) (my + randomNum(0, leaveHeightLength));
        y3 = (int) (my - randomNum(0, leaveHeightLength));
        if (y3 < 0) y3 = 0;

        int msx = sx < ex ? sx : ex;
        float lx = (float) (abs(sx - ex)) / 3.0f;
        x2 = (int) (msx + lx / 2 + randomNum(0, lx));
        x3 = (int) (msx + lx + lx / 2 + randomNum(0, lx));
    } else {
        float mx = (sx + ex) / 2.0f;
        x2 = (int) (mx + randomNum(0, leaveHeightLength));
        x3 = (int) (mx - randomNum(0, leaveHeightLength));
        if (x3 < 0) x3 = 0;

        int msy = sy < ey ? sy : ey;
        float ly = (float) (abs(sy - ey)) / 3.0f;
        y2 = (int) (msy + ly / 2 + randomNum(0, ly));
        y3 = (int) (msy + ly + ly / 2 + randomNum(0, ly));
    }

    return bezierCreate(sx, sy, x2, y2, x3, y3, ex, ey, num, mh, outSize);
}
float randn_bm() {
    float u = 0.0f, v = 0.0f;
    while (u == 0.0f) u = (float) rand() / (float)RAND_MAX;
    while (v == 0.0f) v = (float) rand() /(float) RAND_MAX;
    return sqrtf(-2.0f * logf(u)) * cosf(2.0f * M_PI * v);
}
Point* generate_trajectory(int x1, int y1, int x2, int y2, int nums, int* out_len) {
    if (out_len == NULL || nums <= 0) return NULL;

    int total_points = nums + 2;
    Point* array = (Point*) malloc(sizeof(Point) * total_points);
    if (!array) return NULL;

    float dx = (float)(x2 - x1);
    float dy = (float)(y2 - y1);

    array[0].x = x1;
    array[0].y = y1;

    for (int i = 0; i < nums; i++) {
        float move_ratio = ((float)i + 1) / (float)nums;
        float jitter_x = randn_bm() * 2.0f;
        float jitter_y = randn_bm() * 1.0f;
        float base_x = x1 + dx * move_ratio + jitter_x;
        float base_y = y1 + dy * move_ratio + jitter_y;

        array[i + 1].x = (int)fmaxf(0.0f, roundf(base_x));
        array[i + 1].y = (int)fmaxf(0.0f, roundf(base_y));
    }

    array[total_points - 1].x = x2;
    array[total_points - 1].y = y2;

    *out_len = total_points;
    return array;
}
void println(const char *msg) {
    size_t len = strlen(msg);
    char *buffer = malloc(len + 2);
    if (buffer == NULL) return;
    memcpy(buffer, msg, len);
    buffer[len] = '\n';
    buffer[len + 1] = '\0';
    write(1, buffer, len + 1);
    free(buffer);
}


void inject_bezier(int fd, int x1, int y1, int x2, int y2, int num, int mLeaveHeightLength, int mh,
                   int duration_ms, int will_continue, int pressure) {
    int outSize = 0;
    Point *points = random_points(x1, y1, x2, y2, num, &mLeaveHeightLength, &mh, &outSize);
    if (outSize > 0) {
        int delay = duration_ms / outSize;
        if (will_continue == 0 || will_continue == 3) {
            //mouse down
            send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, 0);
            send_event(fd, EV_ABS, ABS_MT_POSITION_X, x1);
            send_event(fd, EV_ABS, ABS_MT_POSITION_Y, y1);
            send_event(fd, EV_ABS, ABS_MT_PRESSURE, pressure);
            send_event(fd, EV_KEY, BTN_TOUCH, 1);
            send_event(fd, EV_SYN, SYN_REPORT, 0);
            usleep(delay * 1000);
        }
        for (int i = 0; i < outSize; i++) {
            send_event(fd, EV_ABS, ABS_MT_POSITION_X, points[i].x);
            send_event(fd, EV_ABS, ABS_MT_POSITION_Y, points[i].y);
            send_event(fd, EV_SYN, SYN_REPORT, 0);
            usleep(delay * 1000);
        }

        if (will_continue == 2 || will_continue == 3) {
            send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, -1);
            send_event(fd, EV_KEY, BTN_TOUCH, 0);
            send_event(fd, EV_SYN, SYN_REPORT, 0);
        }
        free(points);
    }

}

void inject_stop(int fd, int x, int y,int num,int duration_ms,int pressure,int is_up){
    int delay = duration_ms / num;
    for (int i = 1; i <= num; ++i) {
        send_event(fd, EV_ABS, ABS_MT_WIDTH_MAJOR, pressure);
        send_event(fd, EV_SYN, SYN_REPORT, 0);
        usleep(delay * 1000);
    }
    if(is_up==1){
        send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, -1);
        send_event(fd, EV_KEY, BTN_TOUCH, 0);
        send_event(fd, EV_SYN, SYN_REPORT, 0);
    }
}

void inject_trajectory(int fd, int x1, int y1, int x2, int y2, int num,int duration_ms, int will_continue, int pressure) {
    int outSize = 0;
    Point *points = generate_trajectory(x1, y1, x2, y2, num, &outSize);
    if (outSize > 0) {
        int delay = duration_ms / outSize;
        if (will_continue == 0 || will_continue == 3) {
            //mouse down
            send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, 0);
            send_event(fd, EV_ABS, ABS_MT_POSITION_X, x1);
            send_event(fd, EV_ABS, ABS_MT_POSITION_Y, y1);
            send_event(fd, EV_ABS, ABS_MT_PRESSURE, pressure);
            send_event(fd, EV_KEY, BTN_TOUCH, 1);
            send_event(fd, EV_SYN, SYN_REPORT, 0);
            usleep(delay * 1000);
        }
        for (int i = 0; i < outSize; i++) {
            send_event(fd, EV_ABS, ABS_MT_POSITION_X, points[i].x);
            send_event(fd, EV_ABS, ABS_MT_POSITION_Y, points[i].y);
            send_event(fd, EV_SYN, SYN_REPORT, 0);
            usleep(delay * 1000);
        }

        if (will_continue == 2 || will_continue == 3) {
            send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, -1);
            send_event(fd, EV_KEY, BTN_TOUCH, 0);
            send_event(fd, EV_SYN, SYN_REPORT, 0);
        }
        free(points);
    }

}
// 查找触摸设备（通过 ioctl 检查事件能力）
const char* find_touch_device_by_event_bits() {
    static char path[64];
    static char result[64];
    unsigned char ev_bits[EV_MAX / 8 + 1];
    unsigned char abs_bits[ABS_MAX / 8 + 1];
    unsigned char key_bits[KEY_MAX / 8 + 1];

    for (int i = 0; i < 32; i++) {
        snprintf(path, sizeof(path), "/dev/input/event%d", i);
        int fd = open(path, O_RDONLY);
        if (fd < 0)
            continue;

        memset(ev_bits, 0, sizeof(ev_bits));
        memset(abs_bits, 0, sizeof(abs_bits));
        memset(key_bits, 0, sizeof(key_bits));

        if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0 ||
            ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits) < 0 ||
            ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0) {
            close(fd);
            continue;
        }

        int has_mt = TEST_BIT(ABS_MT_POSITION_X, abs_bits) &&
                     TEST_BIT(ABS_MT_POSITION_Y, abs_bits);
        int has_btn = TEST_BIT(BTN_TOUCH, key_bits);

        if (has_mt || has_btn) {
            LOGI("Found touch device: %s (has_mt=%d, has_btn=%d)", path, has_mt, has_btn);
            close(fd);
            strcpy(result, path);
            return result;
        }

        close(fd);
    }

    LOGI("No touch input device found");
    return NULL;
}

JNIEXPORT void JNICALL Java_com_example_myinput_InputManger_injectBezier(JNIEnv *env,jobject obj,jint x1, jint y1,jint x2, jint y2,jint num,jint mLeaveHeightLength,jint mh,jint durationMs,jint willContinue,jint pressure) {
    const char* devices= find_touch_device_by_event_bits();
    println(devices);
    int fd = open(devices, O_WRONLY);
    if(fd<0){
        println("open fail");
        return;
    }else{
        println("open success");
    }
    inject_bezier(fd, x1, y1, x2, y2, num, mLeaveHeightLength, mh, durationMs, willContinue, pressure);
    close(fd);
    println("touch finish");
}

JNIEXPORT void JNICALL Java_com_example_myinput_InputManger_injectTrajectory(JNIEnv *env,jobject obj,jint x1, jint y1,jint x2, jint y2,jint num,jint durationMs,jint willContinue,jint pressure) {
    const char* devices= find_touch_device_by_event_bits();
    println(devices);
    int fd = open(devices, O_WRONLY);
    if(fd<0){
        println("open fail");
        return;
    }else{
        println("open success");
    }
    inject_trajectory(fd, x1, y1, x2, y2, num,  durationMs, willContinue, pressure);
    close(fd);
    println("touch finish");
}

JNIEXPORT void JNICALL Java_com_example_myinput_InputManger_injectStop(JNIEnv *env,jobject obj,jint x, jint y,jint num,jint durationMs,jint pressure,jint isUp) {
    const char* devices= find_touch_device_by_event_bits();
    println(devices);
    int fd = open(devices, O_WRONLY);
    if(fd<0){
        println("open fail");
        return;
    }else{
        println("open success");
    }
    inject_stop(fd, x, y, num,  durationMs, pressure,isUp);
    close(fd);
    println("touch finish");
}